/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/07/31
*/
#include "Render.h"
#include "../edui_sdl.h"

EDUI_NAMESPACE_BEGIN

Render::Render() : m_render(0), m_texture(0), m_wnd(0)
{}

Render::~Render()
{
    this->Destroy();
}

Render* Render::ToRender(IRender* render)
{
    return dynamic_cast<Render*>(render);
}

IWindow* Render::GetWindow()
{
    return m_wnd;
}

bool Render::Create(IWindow* wnd, int index, RendererFlags flags)
{
    Window* window = Window::ToWindow(wnd);
    if (!window) return false;
    SDL_Window* sdlWnd = window->GetSDLWindow();
    if (!sdlWnd) return false;
    m_render = SDL_CreateRenderer(sdlWnd, index, flags);
    if (!m_render) return false;
    Size sz = this->GetSize();
    m_texture = SDL_CreateTexture(m_render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, sz.w, sz.h);
    if (!m_texture) this->Destroy();
    m_wnd = wnd;
    return true;
}

bool Render::IsOK()
{
    if (!m_render) return false;
    if (!m_texture) return false;
    if (!m_wnd) return false;
    return true;
}

void Render::Destroy()
{
    if (m_texture){
        SDL_DestroyTexture(m_texture);
        m_texture = 0;
    }
    if (m_render){
        SDL_DestroyRenderer(m_render);
        m_render = 0;
    }
    m_wnd = 0;
}

Size Render::GetSize()
{
    Size sz;
    SDL_GetRendererOutputSize(m_render, &sz.w, &sz.h);
    return sz;
}

bool Render::SetTarget(ITexture* texture)
{
    if (!texture){
        return !SDL_SetRenderTarget(m_render, m_texture);
    }
    Texture* image = Texture::ToTexture(texture);
    if (!image) return false;
    return !SDL_SetRenderTarget(m_render, image->GetSDLTexture());
}

ITexture* Render::GetTarget()
{
    SDL_Texture* sdltexture = SDL_GetRenderTarget(m_render);
    if (!sdltexture || (sdltexture == m_texture)) return 0;
    IWindow* wnd = this->GetWindow();
    if (!wnd) return 0;
    TextureMgr* mgr = wnd->GetTextureMgr();
    if (!mgr) return 0;
    for (size_t i = 0; i < mgr->Count(); ++i){
        Texture* texture = Texture::ToTexture(mgr->GetByIndex(i));
        if (texture && (texture->GetSDLTexture() == sdltexture)){
            return texture;
        }
    }
    return 0;
}

bool Render::SetBlendMode(BlendMode mode)
{
    return !SDL_SetRenderDrawBlendMode(m_render, (SDL_BlendMode)mode);
}

BlendMode Render::GetBlendMode()
{
    SDL_BlendMode mode = SDL_BLENDMODE_NONE;
    SDL_GetRenderDrawBlendMode(m_render, &mode);
    return (BlendMode)mode;
}

void Render::Clear(const Color* color)
{
    int w,h;
    SDL_QueryTexture(m_texture, 0, 0, &w, &h);
    Size sz = this->GetSize();
    if ((sz.w != w) || (sz.h != h)){
        SDL_DestroyTexture(m_texture);
        m_texture = SDL_CreateTexture(m_render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, sz.w, sz.h);
    }

    SDL_SetRenderTarget(m_render, m_texture);
    if(color){
        this->SetColor(color->r, color->g, color->b, color->a);
    } else {
        this->SetColor(127, 127, 127, 255);
    }
    SDL_RenderClear(m_render);
}

void Render::Present(const Rect* rc)
{
    SDL_SetRenderTarget(m_render, 0);
    if (!rc){
        SDL_RenderCopy(m_render, m_texture, 0, 0);
    } else {
        SDL_Rect rect = { rc->x, rc->y, rc->w, rc->h };
        SDL_RenderCopy(m_render, m_texture, &rect, &rect);
    }
    SDL_RenderPresent(m_render);
}

bool Render::SetScale(float scaleX, float scaleY)
{
    return !SDL_RenderSetScale(m_render, scaleX, scaleY);
}

PointF Render::GetScale()
{
    PointF pt;
    SDL_RenderGetScale(m_render, &pt.x, &pt.y);
    return pt;
}

bool Render::SetColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
    return !SDL_SetRenderDrawColor(m_render, r, g, b, a);
}

bool Render::SetColor(const Color* color)
{
    if (!color) return false;
    return !SDL_SetRenderDrawColor(m_render, color->r, color->g, color->b, color->a);
}

Color Render::GetColor()
{
    Color col;
    SDL_GetRenderDrawColor(m_render, &col.r, &col.g, &col.b, &col.a);
    return col;
}

bool Render::SetClip(const Rect *rect)
{
    if (!rect || rect->w == 0 || rect->h == 0) return !SDL_RenderSetClipRect(m_render, 0);
    SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
    return !SDL_RenderSetClipRect(m_render, &rc);
}

Rect Render::GetClip()
{
    SDL_Rect rc = {0};
    SDL_RenderGetClipRect(m_render, &rc);
    return Rect(rc.x, rc.y, rc.w, rc.h);
}

bool Render::IsClipEnabled()
{
    return !!SDL_RenderIsClipEnabled(m_render);
}

bool Render::SetViewport(const Rect *rect)
{
    if (!rect) return false;
    SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
    return !SDL_RenderSetViewport(m_render, &rc);
}

Rect Render::GetViewport()
{
    SDL_Rect rc = {0};
    SDL_RenderGetViewport(m_render, &rc);
    return Rect(rc.x, rc.y, rc.w, rc.h);
}

void Render::DrawPoint(int x, int y)
{
    SDL_RenderDrawPoint(m_render, x, y);
}

void Render::DrawLine(int x1, int y1, int x2, int y2)
{
    SDL_RenderDrawLine(m_render, x1, y1, x2, y2);
}

void Render::DrawRect(const Rect * rect)
{
    if (!rect) return;
    SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
    SDL_RenderDrawRect(m_render, &rc);
}

void Render::DrawRect(int x1, int y1, int x2, int y2)
{
    SDL_Rect rc = { x1, y1, x2 - x1, y2 - y1 };
    if (rc.w <=0 || rc.h <=0) return;
    SDL_RenderDrawRect(m_render, &rc);
}

void Render::FillRect(const Rect * rect)
{
    if (!rect) return;
    SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
    SDL_RenderFillRect(m_render, &rc);
}

void Render::FillRect(int x1, int y1, int x2, int y2)
{
    SDL_Rect rc = { x1, y1, x2 - x1, y2 - y1 };
    if (rc.w <=0 || rc.h <=0) return;
    SDL_RenderFillRect(m_render, &rc);
}

void Render::DrawMaskTexture(ISurface* img, ISurface* mask, const Rect* src, const Rect* dst, int id)
{
    if (!img || !mask) return;

    {
        Size sz = img->GetSize(id);
        Size szmask = mask->GetSize();
        if (sz.w != szmask.w || sz.h != szmask.h){
            Log("[EDUI] DrawMaskTexture : image surface must be same to mask surface in width and height.");
            return;
        }
    }

    Uint32 fmtEnum = SDL_PIXELFORMAT_RGBA8888;

    SDL_Surface* maskSurface = SDL_ConvertSurfaceFormat(Surface::ToSurface(mask)->GetSDLSurface(), fmtEnum, 0);
    if (!maskSurface) return;
    SDL_Surface* imgSurface = SDL_ConvertSurfaceFormat(Surface::ToSurface(img)->GetSDLSurface(id), fmtEnum, 0);
    if (!imgSurface){
        SDL_FreeSurface(maskSurface);
        return;
    }
    SDL_Texture* texture = SDL_CreateTexture(m_render, fmtEnum, SDL_TEXTUREACCESS_STREAMING, imgSurface->w, imgSurface->h);
    if (!texture){
        SDL_FreeSurface(maskSurface);
        SDL_FreeSurface(imgSurface);
        return;
    }
    SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);

    void* pixels; int pitch;
    if (SDL_LockSurface(maskSurface) != 0 || SDL_LockSurface(imgSurface) != 0 || SDL_LockTexture(texture, 0, &pixels, &pitch) != 0){
        SDL_UnlockSurface(maskSurface);
        SDL_UnlockSurface(imgSurface);
        SDL_UnlockTexture(texture);
        SDL_FreeSurface(maskSurface);
        SDL_FreeSurface(imgSurface);
        SDL_DestroyTexture(texture);
        return;
    }

    SDL_PixelFormat* fmt = SDL_AllocFormat(fmtEnum);

    Uint8 r, g, b, mr, mg, mb, ma;
    for (int i = 0; i < imgSurface->pitch * imgSurface->h / imgSurface->format->BytesPerPixel; ++i){
        SDL_GetRGB(((Uint32*)imgSurface->pixels)[i], imgSurface->format, &r, &g, &b);
        SDL_GetRGBA(((Uint32*)maskSurface->pixels)[i], maskSurface->format, &mr, &mg, &mb, &ma);
        ((Uint32*)pixels)[i] = SDL_MapRGBA(fmt, r, g, b, ma);
    }
    SDL_UnlockSurface(maskSurface);
    SDL_UnlockSurface(imgSurface);
    SDL_UnlockTexture(texture);

    SDL_FreeSurface(maskSurface);
    SDL_FreeSurface(imgSurface);

    //render
    Size sz = img->GetSize();
    SDL_Rect srcrect = { 0, 0, sz.w, sz.h };
    sz = this->GetSize();
    SDL_Rect dstrect = { 0, 0, sz.w, sz.h };

    if (src){
        srcrect.x = src->x;
        srcrect.y = src->y;
        srcrect.w = src->w;
        srcrect.h = src->h;
    }
    if (dst){
        dstrect.x = dst->x;
        dstrect.y = dst->y;
        dstrect.w = dst->w;
        dstrect.h = dst->h;
    }
    
    SDL_RenderCopy(m_render, texture, &srcrect, &dstrect);

    SDL_DestroyTexture(texture);
    SDL_FreeFormat(fmt);
}

void Render::DrawTexture(ITexture* texture, const Rect* src, const Rect* dst, int id)
{
    if (!texture) return;
    Texture* img = Texture::ToTexture(texture);
    
    Size sz = img->GetSize();
    SDL_Rect srcrect = { 0, 0, sz.w, sz.h };
    sz = this->GetSize();
    SDL_Rect dstrect = { 0, 0, sz.w, sz.h };

    if (src){
        srcrect.x = src->x;
        srcrect.y = src->y;
        srcrect.w = src->w;
        srcrect.h = src->h;
    }
    if (dst){
        dstrect.x = dst->x;
        dstrect.y = dst->y;
        dstrect.w = dst->w;
        dstrect.h = dst->h;
    }
    SDL_RenderCopy(m_render, img->GetSDLTexture(id), &srcrect, &dstrect);
}

void Render::DrawTexture(ITexture* texture, int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2, int id)
{
    if (!texture) return;
    Texture* img = Texture::ToTexture(texture);
    SDL_Rect srcrect = { x1, y1, w1, h1 };
    SDL_Rect dstrect = { x2, y2, w2, h2 };
    SDL_RenderCopy(m_render, img->GetSDLTexture(id), &srcrect, &dstrect);
}

void Render::DrawScaleTexture(ITexture* texture, const Rect* src, const Rect* dst, const Rect1* corner, bool bHole, int id)
{
    if (!texture || !dst) return;
    if (!corner){
        this->DrawTexture(texture, src, dst, id);
        return;
    }

    int x1, y1, w1, h1;
    int x2, y2, w2, h2;
    int l, t, r, b;

    if (!src){
        x1 = y1 = 0;
        Size sz = texture->GetSize();
        w1 = sz.w;
        h1 = sz.h;
    } else {
        x1 = src->x;
        y1 = src->y;
        w1 = src->w;
        h1 = src->h;
    }

    x2 = dst->x;
    y2 = dst->y;
    w2 = dst->w;
    h2 = dst->h;
    
    l = corner->l;
    t = corner->t;
    r = corner->r;
    b = corner->b;

    this->DrawTexture(texture, x1, y1, l, t, x2, y2, l, t, id);
    this->DrawTexture(texture, x1 + w1 -r, y1, r, t, x2 + w2 -r, y2, r, t, id);
    this->DrawTexture(texture, x1, y1 + h1 - b, l, b, x2, y2 + h2 -b, l, b, id);
    this->DrawTexture(texture, x1 + w1 -r, y1 + h1 - b, r, b, x2 + w2 -r, y2 + h2 -b, r, b, id);

    this->DrawTexture(texture, x1 + l, y1, w1 - l - r, t, x2 + l, y2, w2 - l - r, t, id);
    this->DrawTexture(texture, x1, y1 + t, l, h1 - t -b, x2, y2 + t, l, h2 - t - b, id);
    this->DrawTexture(texture, x1 + w1 - r, y1 + t, r, h1 - t - b, x2 + w2 - r, y2 + t, r, h2 - t - b, id);
    this->DrawTexture(texture, x1 + l, y1 + h1 -b, w1 - l -r, b, x2 + l, y2 + h2 - b, w2 - l - r, b, id);
    
    if (!bHole){
        this->DrawTexture(texture, x1 + l, y1 + t, w1 - l - r, h1 - t - b, x2 + l, y2 + t, w2 - l - r, h2 - t - b, id);
    }
}

void Render::DrawText(const char* text, const Rect* rc, IFont* font, const Color* fg)
{
    if (!text ||!rc || !font->IsOK()) return;
    SDL_Color sdlColor = {0};
    if (!fg){
        SDL_GetRenderDrawColor(m_render, &sdlColor.r, &sdlColor.g, &sdlColor.b, &sdlColor.a);
    } else {
        sdlColor.r = fg->r;
        sdlColor.g = fg->g;
        sdlColor.b = fg->b;
        sdlColor.a = fg->a;
    }
    SDL_Surface* surface = TTF_RenderUTF8_Blended((Font::ToFont(font))->GetTTFFont(), text, sdlColor);
    if (!surface) return;

    SDL_Texture* texture = SDL_CreateTextureFromSurface(m_render, surface);
    SDL_FreeSurface(surface);
    surface = 0;
    if (!texture) return;

    Size sz = font->GetSizeUTF8(text);
    if (rc->w < sz.w) sz.w = rc->w;
    if (rc->h < sz.h) sz.h = rc->h;
    
    SDL_Rect srcrc = { 0, 0, sz.w, sz.h };
    SDL_Rect dstrc = { rc->x, rc->y, sz.w, sz.h };
    SDL_RenderCopy(m_render, texture, &srcrc, &dstrc);

    SDL_DestroyTexture(texture);
}

void Render::DrawText(const char* text, int x, int y, IFont* font, const Color* fg)
{
    if (!text || !font->IsOK()) return;
    SDL_Color sdlColor = {0};
    if (!fg){
        SDL_GetRenderDrawColor(m_render, &sdlColor.r, &sdlColor.g, &sdlColor.b, &sdlColor.a);
    } else {
        sdlColor.r = fg->r;
        sdlColor.g = fg->g;
        sdlColor.b = fg->b;
        sdlColor.a = fg->a;
    }
    SDL_Surface* surface = TTF_RenderUTF8_Blended((Font::ToFont(font))->GetTTFFont(), text, sdlColor);
    if (!surface) return;

    SDL_Texture* texture = SDL_CreateTextureFromSurface(m_render, surface);
    SDL_FreeSurface(surface);
    surface = 0;
    if (!texture) return;

    Size sz = font->GetSizeUTF8(text);
    
    SDL_Rect dstrc = { x, y, sz.w, sz.h };
    SDL_RenderCopy(m_render, texture, 0, &dstrc);
    
    SDL_DestroyTexture(texture);
}

void Render::DrawWrappedText(const char* text, const Rect* rc, IFont* font, const Color* fg)
{
    if (!text ||!rc || !font->IsOK()) return;
    SDL_Color sdlColor = {0};
    if (!fg){
        SDL_GetRenderDrawColor(m_render, &sdlColor.r, &sdlColor.g, &sdlColor.b, &sdlColor.a);
    } else {
        sdlColor.r = fg->r;
        sdlColor.g = fg->g;
        sdlColor.b = fg->b;
        sdlColor.a = fg->a;
    }
    SDL_Surface* surface = TTF_RenderUTF8_Blended_Wrapped((Font::ToFont(font))->GetTTFFont(), text, sdlColor, rc->w);
    if (!surface) return;

    SDL_Texture* texture = SDL_CreateTextureFromSurface(m_render, surface);
    SDL_FreeSurface(surface);
    surface = 0;
    if (!texture) return;

    Size sz;
    SDL_QueryTexture(texture, 0, 0, &sz.w, &sz.h);
    if (rc->w < sz.w) sz.w = rc->w;
    if (rc->h < sz.h) sz.h = rc->h;
    
    SDL_Rect srcrc = { 0, 0, sz.w, sz.h };
    SDL_Rect dstrc = { rc->x, rc->y, sz.w, sz.h };
    SDL_RenderCopy(m_render, texture, 0, &dstrc);
    
    SDL_DestroyTexture(texture);
}

SDL_Renderer* Render::GetSDLRender() const
{
    return m_render;
}

EDUI_NAMESPACE_END
